#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
100%还原浏览器行为的 GitCode 登录脚本（requests 版）
支持新建仓库、现有仓库镜像设置、删除仓库
"""
import requests
import json
import re
from urllib.parse import quote, urlparse
import time
import base64

username = "用户名"
password = "密码"

def extract_repo_name_from_github_url(github_url):
    """从GitHub URL中提取仓库名称"""
    # 支持多种GitHub URL格式
    patterns = [
        r'github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$',  # https://github.com/user/repo.git
        r'github\.com/([^/]+)/([^/]+)/?$',             # https://github.com/user/repo
    ]
    
    for pattern in patterns:
        match = re.search(pattern, github_url)
        if match:
            owner, repo_name = match.groups()
            return repo_name
    
    return None

def get_all_repos(session, access_token, username):
    """获取用户的所有仓库"""
    print(f"\n📋 正在获取仓库列表...")
    
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Authorization": f"Bearer {access_token}",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        "X-App-Channel": "gitcode-fe",
        "X-App-Version": "0",
        "X-Device-ID": "unknown",
        "X-Device-Type": "Windows",
        "X-Network-Type": "4g",
        "X-OS-Version": "10",
        "X-Platform": "web"
    }
    
    # 获取仓库列表
    repos_url = f"https://web-api.gitcode.com/api/v1/projects?membership=true&order_by=updated_at&per_page=100&page=1"
    
    try:
        response = session.get(repos_url, headers=headers)
        print(f"获取仓库列表状态码: {response.status_code}")
        print("接口返回内容:", response.text)
        
        if response.status_code == 200:
            repos_data = response.json()
            # 优先从 content 字段获取仓库列表
            if 'content' in repos_data and isinstance(repos_data['content'], list):
                repos = repos_data['content']
            elif 'data' in repos_data:
                data_field = repos_data['data']
                if isinstance(data_field, list):
                    repos = data_field
                elif isinstance(data_field, dict) and 'list' in data_field:
                    repos = data_field['list']
                else:
                    repos = []
            else:
                repos = []
            
            if repos:
                print(f"✅ 找到 {len(repos)} 个仓库:")
                print("-" * 80)
                print(f"{'编号':<4} {'仓库名称':<30} {'可见性':<8} {'更新时间':<20}")
                print("-" * 80)
                
                for i, repo in enumerate(repos, 1):
                    repo_name = repo.get('name', 'Unknown')
                    visibility = repo.get('visibility', 'Unknown')
                    updated_at = repo.get('updated_at', 'Unknown')
                    if updated_at != 'Unknown':
                        updated_at = updated_at[:19].replace('T', ' ')
                    print(f"{i:<4} {repo_name:<30} {visibility:<8} {updated_at:<20}")
                print("-" * 80)
                return repos
            else:
                print("❌ 未找到任何仓库")
                return []
        else:
            print(f"❌ 获取仓库列表失败: {response.text}")
            return []
            
    except Exception as e:
        print(f"❌ 获取仓库列表异常: {e}")
        return []

def delete_repo_by_number(session, access_token, username):
    """根据编号删除仓库，支持循环删除，输入q退出"""
    while True:
        repos = get_all_repos(session, access_token, username)
        if not repos:
            print("❌ 没有可删除的仓库")
            print("💡 提示：你可以先选择选项1创建一些测试仓库")
            return False
        print(f"\n🗑️ 请输入要删除的仓库编号 (1-{len(repos)})，或输入q退出:")
        try:
            choice = input("仓库编号: ").strip()
            if choice.lower() == 'q':
                print("👋 已退出删除操作")
                break
            repo_index = int(choice) - 1
            if 0 <= repo_index < len(repos):
                repo = repos[repo_index]
                repo_name = repo.get('name')
                repo_id = repo.get('id')
                print(f"\n⚠️ 确认删除仓库: {repo_name}")
                print(f"仓库ID: {repo_id}")
                print(f"仓库主页: {repo.get('web_url')}")
                print("确认删除? (输入 'yes'/'y'/'YES'/'Yes'/'确认'/'同意' 确认): ")
                confirm = input().strip().lower()
                if confirm in ['yes', 'y', '确认', '同意']:
                    # 调用删除接口
                    result = delete_repo(session, access_token, repo_id, repo_name)
                    if result:
                        print(f"✅ 删除请求已处理，返回仓库列表...\n")
                    else:
                        print(f"❌ 删除失败，返回仓库列表...\n")
                else:
                    print("❌ 取消删除操作，返回仓库列表...\n")
            else:
                print(f"❌ 无效的仓库编号，请输入 1-{len(repos)} 之间的数字")
        except ValueError:
            print("❌ 请输入有效的数字")
        except Exception as e:
            print(f"❌ 输入异常: {e}")

def delete_repo(session, access_token, repo_id, repo_name):
    """删除指定仓库，兼容异步删除提示"""
    print(f"\n🗑️ 正在删除仓库: {repo_name}")
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Authorization": f"Bearer {access_token}",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        "X-App-Channel": "gitcode-fe",
        "X-App-Version": "0",
        "X-Device-ID": "unknown",
        "X-Device-Type": "Windows",
        "X-Network-Type": "4g",
        "X-OS-Version": "10",
        "X-Platform": "web"
    }
    delete_url = f"https://web-api.gitcode.com/api/v1/projects/{repo_id}"
    try:
        response = session.delete(delete_url, headers=headers)
        print(f"删除仓库状态码: {response.status_code}")
        if response.status_code == 204:
            print(f"✅ 仓库 {repo_name} 删除成功!")
            return True
        elif response.status_code == 200:
            try:
                resp_json = response.json()
                if resp_json.get('message') == '202 Accepted':
                    print(f"⚠️ 仓库删除请求已提交，GitCode 正在后台处理，稍后仓库会自动消失。")
                    return True
            except Exception:
                pass
            print(f"❌ 删除仓库失败: {response.text}")
            return False
        else:
            print(f"❌ 删除仓库失败: {response.text}")
            return False
    except Exception as e:
        print(f"❌ 删除仓库异常: {e}")
        return False

def delete_all_repos(session, access_token, username):
    """删除所有仓库"""
    print(f"\n⚠️ 危险操作：删除所有仓库")
    print("=" * 50)
    print("此操作将删除您的所有仓库，且无法恢复！")
    print("请确认您真的要删除所有仓库吗？")
    print("=" * 50)
    
    confirm = input("请输入 'DELETE_ALL' 确认删除所有仓库: ").strip()
    if confirm != 'DELETE_ALL':
        print("❌ 操作已取消")
        return False
    
    # 获取所有仓库
    repos = get_all_repos(session, access_token, username)
    if not repos:
        print("❌ 没有可删除的仓库")
        return False
    
    print(f"\n🗑️ 准备删除 {len(repos)} 个仓库...")
    
    # 再次确认
    print("=" * 50)
    print("即将删除以下仓库：")
    for i, repo in enumerate(repos, 1):
        repo_name = repo.get('name', 'Unknown')
        print(f"{i}. {repo_name}")
    print("=" * 50)
    
    final_confirm = input("确认删除以上所有仓库？(输入 'YES_DELETE_ALL' 确认): ").strip()
    if final_confirm != 'YES_DELETE_ALL':
        print("❌ 操作已取消")
        return False
    
    # 开始删除
    success_count = 0
    failed_count = 0
    
    for i, repo in enumerate(repos, 1):
        repo_name = repo.get('name')
        repo_id = repo.get('id')
        print(f"\n[{i}/{len(repos)}] 正在删除仓库: {repo_name}")
        
        result = delete_repo(session, access_token, repo_id, repo_name)
        if result:
            success_count += 1
        else:
            failed_count += 1
        
        # 添加延迟避免请求过快
        time.sleep(1)
    
    print(f"\n🎯 删除完成！")
    print(f"✅ 成功删除: {success_count} 个仓库")
    print(f"❌ 删除失败: {failed_count} 个仓库")
    
    return success_count > 0

def create_test_repo(session, access_token, username):
    """创建测试仓库"""
    print("\n🚀 请输入测试仓库名称:")
    repo_name = input("仓库名称: ").strip()
    
    if not repo_name:
        print("❌ 未输入仓库名称")
        return False
    
    print(f"🔨 开始创建测试仓库 {repo_name}...")
    
    create_repo_headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {access_token}",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
    }
    
    repo_data = {
        "name": repo_name,
        "description": f"测试仓库 - {repo_name}",
        "visibility": "public",
        "initialize_with_readme": True
    }
    
    create_response = session.post(
        "https://web-api.gitcode.com/api/v1/projects",
        headers=create_repo_headers,
        json=repo_data
    )
    
    print(f"创建仓库状态码: {create_response.status_code}")
    
    if create_response.status_code in [200, 201]:
        print("🎉 测试仓库创建成功!")
        return True
    elif create_response.status_code == 422:
        print("⚠️ 仓库已存在")
        return False
    else:
        print(f"❌ 创建仓库失败: {create_response.text}")
        return False

def create_new_repo_with_mirror(session, access_token, username, github_url):
    """创建新仓库并设置镜像"""
    repo_name = extract_repo_name_from_github_url(github_url)
    if repo_name:
        print(f"📦 提取到仓库名称: {repo_name}")
        
        # 创建仓库
        print(f"🔨 开始创建仓库 {repo_name}...")
        
        create_repo_headers = {
            "Accept": "application/json, text/plain, */*",
            "Content-Type": "application/json",
            "Authorization": f"Bearer {access_token}",
            "Referer": "https://gitcode.com/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        }
        
        repo_data = {
            "name": repo_name,
            "description": f"Mirror of {github_url}",
            "visibility": "public",
            "initialize_with_readme": True
        }
        
        create_response = session.post(
            "https://web-api.gitcode.com/api/v1/projects",
            headers=create_repo_headers,
            json=repo_data
        )
        
        print(f"创建仓库状态码: {create_response.status_code}")
        
        if create_response.status_code in [200, 201]:
            repo_info = create_response.json()
            print("🎉 仓库创建成功!")
            print(f"仓库名称: {repo_info.get('name')}")
            print(f"仓库主页: {repo_info.get('web_url')}")
            print(f"SSH地址: {repo_info.get('ssh_url_to_repo')}")
            print(f"HTTP地址: {repo_info.get('http_url_to_repo')}")
            print(f"描述: {repo_info.get('description')}")
            print(f"可见性: {repo_info.get('visibility')}")

            # 新建仓库后，循环获取仓库列表，确认新仓库已添加
            found = False
            for i in range(10):  # 最多等待10次，每次3秒
                print(f"⏳ 检查新仓库是否已出现在列表中（第{i+1}次）...")
                repos = get_all_repos(session, access_token, username)
                if any(r.get('name') == repo_name for r in repos):
                    found = True
                    print(f"✅ 新仓库已出现在列表中，等待10秒后设置镜像...")
                    time.sleep(10)
                    break
                time.sleep(3)
            if not found:
                print(f"⚠️ 新仓库未在30秒内出现在列表中，仍尝试设置镜像...")
            # 设置镜像
            return setup_mirror_for_repo(session, access_token, username, repo_name, github_url)
        else:
            print(f"❌ 创建仓库失败: {create_response.text}")
            return False
    else:
        print("❌ 无法从GitHub URL中提取仓库名称")
        return False

def setup_mirror_for_repo(session, access_token, username, repo_name, github_url):
    """为仓库设置镜像"""
    print(f"\n🔄 开始设置镜像仓库...")
    
    # 先访问仓库主页、设置页、镜像设置页，模拟真实用户操作
    repo_home_url = f"https://gitcode.com/{username}/{repo_name}"
    repo_setting_url = f"https://gitcode.com/{username}/{repo_name}/setting"
    repo_mirror_url = f"https://gitcode.com/{username}/{repo_name}/setting/mirror"
    session.get(repo_home_url)
    session.get(repo_setting_url)
    session.get(repo_mirror_url)
    
    # 构建镜像API URL
    repo_path = f"{username}%2F{repo_name}"  # 使用%2F而不是/
    mirror_url = f"https://web-api.gitcode.com/api/v2/projects/{repo_path}/repo_remote_mirror"
    
    # 镜像请求头
    mirror_headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {access_token}",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        "X-App-Channel": "gitcode-fe",
        "X-App-Version": "0",
        "X-Device-ID": "unknown",
        "X-Device-Type": "Windows",
        "X-Network-Type": "4g",
        "X-OS-Version": "10",
        "X-Platform": "web"
    }
    # 自动拼接 Cookie 字段
    cookie_str = "; ".join([f"{k}={v}" for k, v in session.cookies.get_dict().items()])
    mirror_headers["Cookie"] = cookie_str
    
    # 先获取镜像信息
    print(f"🔍 获取当前镜像信息...")
    mirror_get_response = session.get(
        mirror_url,
        headers=mirror_headers
    )
    
    print(f"获取镜像信息状态码: {mirror_get_response.status_code}")
    
    if mirror_get_response.status_code == 200:
        mirror_info = mirror_get_response.json()
        print("✅ 获取镜像信息成功!")
        print(f"当前镜像URL: {mirror_info.get('url')}")
        print(f"当前镜像状态: {'启用' if mirror_info.get('enabled') else '禁用'}")
        
        # 检查是否需要配置镜像源
        if not mirror_info.get('url') or not mirror_info.get('enabled'):
            print(f"\n🔄 需要配置镜像源...")
            
            # 确保GitHub URL格式正确
            if github_url.endswith('.git'):
                mirror_url_clean = github_url
            else:
                mirror_url_clean = github_url + '.git'

            encoded_url = base64.b64encode(mirror_url_clean.encode()).decode()

            mirror_update_data = {
                "url": encoded_url,
                "force": True
            }
            
            print(f"更新镜像配置: {mirror_url}")
            print(f"镜像数据: {json.dumps(mirror_update_data, indent=2, ensure_ascii=False)}")
            
            mirror_update_response = session.post(
                mirror_url,
                headers=mirror_headers,
                json=mirror_update_data
            )
            
            print(f"更新镜像配置状态码: {mirror_update_response.status_code}")
            print(f"更新镜像配置响应: {mirror_update_response.text}")
            
            if mirror_update_response.status_code == 200:
                print("✅ 镜像配置更新成功!")
            else:
                print(f"⚠️ 镜像配置更新失败: {mirror_update_response.text}")
                print("⚠️ 需要手动在GitCode界面设置镜像")
                return False
        else:
            print("✅ 镜像源已正确配置!")
        
        # 同步镜像仓库
        print(f"\n🔄 开始同步镜像仓库...")
        
        sync_url = f"https://web-api.gitcode.com/api/v2/projects/{repo_path}/sync_remote_mirror"
        
        sync_response = session.post(
            sync_url,
            headers=mirror_headers,
            json={}  # 空JSON对象
        )
        
        print(f"同步状态码: {sync_response.status_code}")
        print(f"同步响应: {sync_response.text}")
        
        if sync_response.status_code == 200:
            print("✅ 镜像同步成功!")
            print(f"🎉 镜像设置完成! 仓库 {repo_name} 已成功设置镜像并同步")
            return True
        else:
            print(f"⚠️ 镜像同步失败: {sync_response.text}")
            return False
    else:
        print(f"❌ 获取镜像信息失败: {mirror_get_response.text}")
        print("⚠️ 可能仓库不存在或没有镜像权限")
        return False

def setup_mirror_for_existing_repo(session, access_token, username):
    """为现有仓库设置镜像"""
    print("\n🚀 请输入现有GitCode仓库名称:")
    repo_name = input("仓库名称: ").strip()
    
    print("\n🚀 请输入GitHub仓库地址:")
    github_url = input("GitHub URL: ").strip()
    
    if repo_name and github_url:
        print(f"📦 仓库名称: {repo_name}")
        print(f"📦 GitHub地址: {github_url}")
        
        return setup_mirror_for_repo(session, access_token, username, repo_name, github_url)
    else:
        print("❌ 未输入仓库名称或GitHub地址")
        return False

def main():
    # 1. 登录参数

    # 密码先Base64编码再URL编码（已知抓包结果为8OaI64xWuGi9LUKQfopc3A%3D%3D）
    encoded_password = "8OaI64xWuGi9LUKQfopc3A%3D%3D"

    # 2. POST数据
    post_data = f"username={quote(username)}&password={encoded_password}"

    # 3. 请求头（完全对齐抓包）
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/x-www-form-urlencoded",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        "X-App-Channel": "gitcode-fe",
        "X-App-Version": "0",
        "X-Device-ID": "unknown",
        "X-Device-Type": "Windows",
        "X-Network-Type": "4g",
        "X-OS-Version": "10",
        "X-Platform": "web",
        "X-Request-ID": "unknown",
        "X-User-ID": "unknown"
    }

    # 4. 创建session并发送登录请求
    session = requests.Session()
    session.headers.update(headers)

    print("🔑 正在登录 GitCode...")
    response = session.post("https://web-api.gitcode.com/uc/api/v1/user/oauth/login", data=post_data)

    print(f"状态码: {response.status_code}")
    print(f"响应内容: {response.text}")

    if response.status_code == 200:
        login_data = response.json()
        print("✅ 登录成功!")
        print(f"用户名: {login_data.get('username')}")
        print(f"邮箱: {login_data.get('email')}")
        print(f"用户ID: {login_data.get('id')}")
        
        # 获取access_token
        access_token = login_data.get('access_token')
        user_id = login_data.get('id')
        
        if access_token:
            print(f"✅ 获取到 access_token: {access_token[:50]}...")
            
            # 5. 选择操作类型
            print("\n🚀 请选择操作类型:")
            print("1. 新建仓库并设置镜像 (默认)")
            print("2. 为现有仓库添加镜像")
            print("3. 删除仓库")
            print("4. 创建测试仓库")
            print("5. 删除所有仓库 ⚠️")
            
            choice = input("请选择 (1/2/3/4/5，直接回车选择1): ").strip()
            
            if choice == "2":
                # 为现有仓库添加镜像
                setup_mirror_for_existing_repo(session, access_token, username)
            elif choice == "3":
                # 删除仓库
                delete_repo_by_number(session, access_token, username)
            elif choice == "4":
                # 创建测试仓库
                create_test_repo(session, access_token, username)
            elif choice == "5":
                # 删除所有仓库
                delete_all_repos(session, access_token, username)
            else:
                # 新建仓库并设置镜像
                print("\n🚀 请输入GitHub仓库地址:")
                github_url = input("GitHub URL: ").strip()
                
                if github_url:
                    create_new_repo_with_mirror(session, access_token, username, github_url)
                else:
                    print("❌ 未输入GitHub地址")
        else:
            print("❌ 未获取到access_token")
    else:
        print("❌ 登录失败!")
        print(f"错误信息: {response.text}")

if __name__ == "__main__":
    main() 